#!/bin/bash
# PacBuilder - By Andrea Cimitan
#
# Copyright (C) 2008-2009 Andrea Cimitan <andrea.cimitan@gmail.com>
#                         Andrea Scarpino <bash.lnx@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

# BEGIN_SECTION - gettext initialization

export TEXTDOMAIN='pacbuilder'
export TEXTDOMAINDIR='/usr/share/locale'

# END_SECTION - gettext initialization

# BEGIN_SECTION - environment checks

function launch_with_su()
{
  # try to launch $1 with sudo, else prompt for root password
  #msg "try to launch '${@}' with sudo"
  command=`echo $* | awk '{print $1}'`

  if [ $SUDOINSTALLED -eq 1 ] && sudo -l | grep "\(${command}\ *$\|ALL\)" 1>/dev/null; then
    sudo $@ || return 1
  else
    UID_ROOT=0
    if [ "$UID" -ne "$UID_ROOT" ]; then
      echo -e $(eval_gettext 'You are not allowed to launch $command with sudo\nPlease enter root password')
    fi
    return 1
  fi
}

function check_required_bin()
{
  if ! which $1 >/dev/null 2>&1; then
    printf "$(gettext "You need to install %s!")\n" "$1"
    exit 1
  fi
}

function check_required_binaries()
{
  check_required_bin "pacman"
  check_required_bin "makepkg"
  check_required_bin "rsync"
  check_required_bin "tar"
  check_required_bin "wget"
}

function colorize_output()
{
  if [ "$NOCOLOR" != "TRUE" ]; then
    DEFA="\033[1;0m"
    BOLD="\033[1;1m"
    LRED="\033[1;31m"
    LGRE="\033[1;32m"
    YELL="\033[1;33m"
    LBLU="\033[1;34m"
  else
    NOCOLOR_FLAG="--nocolor"
  fi
}

function environment()
{
  AUTHOR_NAME="Andrea Cimitan"
  AUTHOR_EMAIL="andrea.cimitan@gmail.com"
  CMD_BIN="pacbuilder"
  CMD_NAME="PacBuilder"
  LICENSE="GPLv3"

  source /etc/makepkg.conf
  if [ -r ~/.makepkg.conf ]; then
    source ~/.makepkg.conf
  fi
  source /etc/${CMD_BIN}.conf
  if [ -r ~/.${CMD_BIN}.conf ]; then
    source ~/.${CMD_BIN}.conf
  fi

  [ -z "$TMP_PATH" ] && TMP_PATH=/tmp
  [ -z "$LOG_FILE" ] && LOG_FILE=~/${CMD_BIN}-packages.log
  [ -z "$PKGDEST" ] && PKGDEST="."
  BUILD_DIR=${TMP_PATH}/${CMD_BIN}/build
  SYNCCMD="rsync"
  SYNCARGS="-mrtv --no-motd --delete-after --delete-excluded"
  SYNCSERVER="rsync.archlinux.org"
  PACMANBIN="/usr/bin/pacman"
  PACMANROOT=`LC_ALL=C $PACMANBIN --verbose \
              | grep 'DB Path' \
              | awk '{print $4}' \
              | sed "s/\/$//"`

  mkdir -p $BUILD_DIR
  if [ `type -p sudo` ]; then SUDOINSTALLED=1; fi
}

# END_SECTION - environment checks
# BEGIN_SECTION - command line options

function add_repository()
{
  ALREADY_ADDED="FALSE"
  for ADDED_REPO in $TARGET_REPO; do
    if [ "$ADDED_REPO" == "$1" ]; then
      ALREADY_ADDED="TRUE"
    fi
  done
  if [ "$ALREADY_ADDED" != "TRUE" ]; then
    TARGET_REPO=${TARGET_REPO}${1}" "
    REPO_ENABLED="TRUE"
  fi
}

function add_type()
{
  ALREADY_ADDED="FALSE"
  for ADDED_TYPE in $ENABLED_TYPES; do
    if [ "$ADDED_TYPE" == "$1" ]; then
      ALREADY_ADDED="TRUE"
    fi
  done
  if [ "$ALREADY_ADDED" != "TRUE" ]; then
    ENABLED_TYPES=${ENABLED_TYPES}${1}" "
    TYPES_ENABLED="TRUE"
  fi
}

function set_regex()
{
  PKG_MATCH="TRUE"
  PKG_REGEX="$@"
}

function check_args()
{
  if [ -z "$1" ]; then
    NOCOLOR="TRUE"
    print_help
    exit 0
  fi

  while [ $# -gt 0 ]; do
    OPTIND=0
    for EXTRA_FLAG in $@; do
      case $EXTRA_FLAG in
        -*) break ;;
         *) INSTALLPKGS=${INSTALLPKGS}${EXTRA_FLAG}" " ; shift ;;
      esac
    done
    while getopts ":Sbpndefksuvhm:-:" ARGS ; do
      case $ARGS in
        S) INSTALLING="TRUE" ;;
        b) BUILDDEPS="TRUE" ;;
        p) PRETEND="TRUE" ;;
        n) NOCONFIRM="TRUE" ;;
        d) FULLDEPLIST="TRUE" ;;
        e) VERBOSE="TRUE" ; EDIT_PKGBUILD="TRUE" ;;
        f) FORCE="TRUE" ;;
        k) KEEPDEPS="TRUE" ;;
        u) SYSUPGRADE="TRUE" ;;
        v) VERBOSE="TRUE" ;;
        h) HELP="TRUE" ;;
        m) set_regex $OPTARG ;;
        s) SEARCH="TRUE" ; shift $(($OPTIND - 1)); set_regex $1; shift; OPTIND=1 ;;
        -)
        case $OPTARG in
          core) add_repository "core" ;;
          extra) add_repository "extra" ;;
          testing) add_repository "testing" ;;
          community) add_repository "community" ;;
          community-testing) add_repository "community-testing" ;;
          aur) add_repository "aur" ;;
          repo)
          shift $(($OPTIND - 1))
          add_repository "$1"
          shift
          OPTIND=1
          ;;

          help) HELP="TRUE" ;;
          verbose) VERBOSE="TRUE" ;;
          pretend) PRETEND="TRUE" ;;
          noconfirm) NOCONFIRM="TRUE" ;;
          builddeps) BUILDDEPS="TRUE" ;;
          deplist) FULLDEPLIST="TRUE" ;;
          edit) VERBOSE="TRUE" ; EDIT_PKGBUILD="TRUE" ;;
          gccinfo) GCCINFO="TRUE" ;;
          nocolor) NOCOLOR="TRUE" ;;
          notitle) NOTITLE="TRUE" ;;
          clean) CLEAN="TRUE" ;;
          force) FORCE="TRUE" ;;
          keepdeps) KEEPDEPS="TRUE" ;;
          noresume) NORESUME="TRUE" ;;
          sysupgrade) SYSUPGRADE="TRUE" ;;
          install) INSTALLING="TRUE" ;;
          deps) add_type "deps" ;;
          explicit) add_type "explicit" ;;
          world) add_type "world" ;;
          devel) add_type "devel" ;;
          export) shift ; EXPORT_DIR=$1 ;;
          match)
          shift $(($OPTIND - 1))
          set_regex $1
          shift
          OPTIND=1
          ;;
          search) SEARCH="TRUE"
          shift $(($OPTIND - 1))
          set_regex $1
          shift
          OPTIND=1
          ;;
          *) NOCOLOR="TRUE"; print_help; exit 0 ;;
        esac
        ;;
        ?) NOCOLOR="TRUE"; print_help; exit 0 ;;
        *) echo "and $OPTARG" ;;
      esac
    done
    shift $(($OPTIND - 1))
  done
}

# END_SECTION - command line options
# BEGIN_SECTION - print to stdout

msg()
{
  local mesg=$1; shift
  printf "==> ${mesg}\n" "$@" >&2
}

function print_building()
{
  printf "${LGRE}==>${DEFA}"
  printf "${BOLD} $(gettext "Building %s")" "$CURRENT_PKG"
  if [ -n "$DEPENDENCY_OF" ]; then
    printf "${DEFA} [$DEPENDENCY_OF] ... "
  else
    printf "${DEFA} [$(gettext "package %s of %d")] ... " "$INDEX_PKG" "$NUM_PKGS"
  fi
}

function print_building_dep()
{
  if [ $INDEX_DEP -gt 1 ]; then
    print_building
  fi
  printf "${YELL}$(gettext "depends on %s!")${DEFA}\n" "$1"
}

function print_checkinstall()
{
  if [ $INDEX_DEP -gt 0 ]; then
    print_building
  fi
  if [ $1 -eq 1 ]; then
    if [ $2 -eq 1 ]; then
      printf "${LRED}$(gettext "missing dependency!")${DEFA}\n"
    elif [ $3 -eq 2 ]; then
      printf "${LRED}$(gettext "missing make-dependency!")${DEFA}\n"
      printf "$(gettext "The following packages are required for compilation"):\n    ${MAKEDEPS_MISSING}\n"
    elif [ $4 -eq 1 ]; then
      printf "${LRED}$(gettext "PKGBUILD not found!")${DEFA}\n"
    elif [ $5 -eq 1 ]; then
      printf "${YELL}$(gettext "is a group, checking for elements")${DEFA}\n"
    else
      printf "${LRED}$(gettext "makepkg failed!")${DEFA}\n"
    fi
  else
    printf "${LGRE}$(gettext "installed!")${DEFA}\n"
  fi
}

function print_checkuninstall()
{
  if [ $1 -eq 1 ]; then
    printf "${LRED}$(gettext "error!")${DEFA}\n"
  else
    printf "${LGRE}$(gettext "uninstalled!")${DEFA}\n"
  fi
}

function print_clean()
{
  printf "${YELL}==> $(gettext "WARNING:")${DEFA}"
  printf "${BOLD} $(gettext "Do you want to delete your log file and build directory?")\n"
  printf "$(gettext "You will not be able to resume it in the future.")"
  printf " $(gettext "Are you sure? (y/N):")${DEFA}"
}

function print_edit_pkgbuild()
{
  printf "${YELL}==> $(gettext "EDIT PKGBUILD:")${DEFA}"
  printf "${BOLD} $(gettext "Do you want to edit this PKGBUILD? (Y/n):")${DEFA} "
}

function print_errorpkgs()
{
  printf "${LRED}==> $(gettext "ERROR:")${DEFA}"
  printf "${BOLD} $(gettext "I was unable to install these packages:")"
  printf "${DEFA} $ERROR_PKGS\n"
}

function print_help()
{
  printf "$(gettext "A tool to massively recompile packages from sources")\n"
  printf "$(gettext "It currently fetches both ABS and AUR")\n"
  printf "\n"
  printf "$(gettext "USAGE:")\n"
  printf "  $(gettext "%s [options] package|repository")\n" "$CMD_BIN"
  printf "\n"
  printf "$(gettext "OPTIONS:")\n"
  printf "  $(gettext "General:")\n"
  printf "  $(gettext "  --help                  print this help")\n"
  printf "  $(gettext "  --clean                 remove previous log")\n"
  printf "  $(gettext "  --gccinfo               print current compilation flags")\n"
  printf "  $(gettext "  --nocolor               do not use any color")\n"
  printf "  $(gettext "  --notitle               do not print the title")\n"
  printf "  $(gettext "  --noresume              do not resume")\n"
  printf "  $(gettext "Install:")\n"
  printf "  $(gettext "(-S),    --install        build specified packages")\n"
  printf "  $(gettext "(-S) -b, --builddeps      build and install the dependencies")\n"
  printf "  $(gettext "(-S) -e, --edit           be verbose and edit PKGBUILD")\n"
  printf "  $(gettext "(-S) -f, --force          force install, overwrite conflicting files")\n"
  printf "  $(gettext "(-S) -k, --keepdeps       keep makedepends after install")\n"
  printf "  $(gettext "(-S) -s, --search <regex> search for packages matching <regex>")\n"
  printf "  $(gettext "(-S) -u, --sysupgrade     build the updated packages")\n"
  printf "  $(gettext "(-S) -v, --verbose        print makepkg output")\n"
  printf "  $(gettext "Additional parameters:")\n"
  printf "  $(gettext "  -p, --pretend           print the final list of packages to be installed")\n"
  printf "  $(gettext "  -n, --noconfirm         do not ask for any confirmation")\n"
  printf "  $(gettext "  -m, --match <regex>     only install packages matching <regex>")\n"
  printf "  $(gettext "  -d, --deplist           recursively list all dependencies first")\n"
  printf "  $(gettext "      --export <dir>      copy packages into a dir after build")\n"
  printf "  $(gettext "Type:")\n"
  printf "  $(gettext "  --world                 recompile both deps and explicit")\n"
  printf "  $(gettext "  --explicit              recompile explicitely installed packages")\n"
  printf "  $(gettext "  --devel                 recompile only installated devel packages")\n"
  printf "  $(gettext "Target repository:")\n"
  printf "  $(gettext "  --core                  recompile packages in core")\n"
  printf "  $(gettext "  --extra                 recompile packages in extra")\n"
  printf "  $(gettext "  --testing               recompile packages in testing")\n"
  printf "  $(gettext "  --community             recompile packages in community")\n"
  printf "  $(gettext "  --community-testing     recompile packages in community-testing")\n"
  printf "  $(gettext "  --aur                   recompile packages in aur")\n"
}

function print_gcc_info()
{
  printf "${BOLD}Carch:${DEFA} ${CARCH}\n"
  printf "${BOLD}Chost:${DEFA} ${CHOST}\n"
  printf "${BOLD}Cflags:${DEFA} ${CFLAGS}\n"
  printf "${BOLD}Cxxflags:${DEFA} ${CXXFLAGS}\n"
  printf "${BOLD}Makeflags:${DEFA} ${MAKEFLAGS}\n"
  printf "\n"
}

function print_installing()
{
  printf "${LGRE}==>${DEFA}"
  printf "${BOLD} $(gettext "Installing %s")" "$pkgname-$pkgver-$pkgrel"
  if [ -n "$DEPENDENCY_OF" ]; then
    printf "${DEFA} [$DEPENDENCY_OF] ... "
  else
    printf "${DEFA} [$(gettext "package %s of %d")] ..." "$INDEX_PKG" "$NUM_PKGS"
  fi
}

function print_match_repo()
{
  printf "$(gettext "Fetching repositories, this may take a long time...")"
}

function print_no_log_file()
{
  printf "${LRED}==> $(gettext "ERROR:")${DEFA}"
  printf "${BOLD} $(gettext "I was unable to find any package to build...")"
  printf "${LRED} $(gettext "exiting!")${DEFA}\n"
}

function print_refreshing_database()
{
  printf "$(gettext "Refreshing pacman database, please wait...")"
}

function print_repo()
{
  printf "${BOLD}$(gettext "Repository: ")${DEFA} $1\n"
}

function print_resume_detected()
{
  printf "${YELL}==> $(gettext "WARNING:")${DEFA}"
  printf "${BOLD} $(gettext "Previous build detected.")"
  printf " $(gettext "Do you want to resume? (Y/n):")${DEFA} "
}

function print_resume_overwrite()
{
  printf "${YELL}==> $(gettext "WARNING:")${DEFA}"
  printf "${BOLD} $(gettext "Your previous build will be overwritten.")"
  printf " $(gettext "Continue? (y/N):")${DEFA} "
}

function print_success()
{
  printf "${LGRE} $1!${DEFA}\n"
}

function print_title()
{
  if [ "$NOCOLOR" != "TRUE" ]; then
    printf "${LBLU}-------------------------------\n"
    printf "${YELL} $CMD_NAME${DEFA}, by $AUTHOR_NAME ${LBLU}\n"
    printf "${LBLU}-------------------------------\n"
    printf "${DEFA}\n"
  else
    printf "\033[1;0m-------------------------------\n"
    printf " $CMD_NAME, by $AUTHOR_NAME\n"
    printf "\033[1;0m-------------------------------\n"
    printf "\n"
  fi
}

function print_type()
{
  printf "${BOLD}$(gettext "Type:")${DEFA} $1\n"
}

function print_uninstalling_deps()
{
  printf "${YELL}==>${DEFA}"
  printf "${BOLD} $(gettext "Uninstalling dependencies:")${DEFA} $MAKEDEPS_INSTALLED... "
}

# END_SECTION - print to stdout
# BEGIN_SECTION - pkgbuild parsing and editing

function edit_pkgbuild()
{
  if [ "$VERBOSE" == "TRUE" ]; then
    if [ "$EDIT_PKGBUILD" == "TRUE" ]; then
      printf "\n"
      if [ -f ./PKGBUILD ]; then
        print_edit_pkgbuild
        read EDIT_ANSWER
        if [ "$EDIT_ANSWER" != "n" ]; then
          [ -z "$EDITOR" ] && EDITOR=nano
          $EDITOR ./PKGBUILD
        fi
      fi
    fi
  fi
}

function get_json_version()
{
  wget -q -O - "http://aur.archlinux.org/rpc.php?type=info&arg=${1}" \
  | sed -e 's/^.*{*"Version":"//' -e 's/",".*$//'
}

function get_aur_pkgrel()
{
  fetch_json $1 | awk -F"-" '{ print $1 }'
}

function get_aur_pkgver()
{
  fetch_json $1 | awk -F"-" '{ print $1 }'
}

function get_package_depends()
{
  unset depends pkgname DEPS
  CHECK_DEP=0
  source ./PKGBUILD
  for DEP in $(echo "${depends[@]}" | tr -d '\\'); do
    DEPS[${#DEPS[@]}]=$(echo $DEP | sed 's/=.*//' \
                                  | sed 's/>.*//' \
                                  | sed 's/<.*//')
  done
  [ ${#DEPS[@]} -eq 0 ] && return 0
  for DEP in ${DEPS[@]}; do
    if [ "$DEP" != "$pkgname" ] &&
       ! is_installed $DEP &&
       ! is_provided $DEP; then
      [ "$BUILDDEPS" != "TRUE" ] && return 1
      (( INDEX_DEP++ ))
      print_building_dep $DEP
      LOG_FILE=${TMP_PATH}/${CMD_BIN}/${CMD_BIN}-${pkgname}.log \
      NOCOLOR=$NOCOLOR VERBOSE=$VERBOSE EDIT_PKGBUILD=$EDIT_PKGBUILD \
      GCCINFO="" DEPENDENCY_OF="dependency of $pkgname" PACMAN_FLAGS="--asdeps"
      if [ "$NOCONFIRM" != "TRUE" ]; then
        $CMD_BIN --notitle --noresume --builddeps --install $DEP || CHECK_DEP=1
      else
        $CMD_BIN --notitle --noresume --noconfirm --builddeps --install $DEP || CHECK_DEP=1
      fi
      rm -rf $LOG_FILE
      [ $CHECK_DEP -eq 1 ] && return 1
    fi
  done
  return 0
}

function get_package_makedepends()
{
  unset makedepends pkgname MAKEDEPS
  CHECK_MAKEDEP=0
  source ./PKGBUILD
  for MAKEDEP in $(echo "${makedepends[@]}" | tr -d '\\'); do
    MAKEDEPS[${#MAKEDEPS[@]}]=$(echo $MAKEDEP | sed 's/=.*//' \
                                              | sed 's/>.*//' \
                                              | sed 's/<.*//')
  done
  [ ${#MAKEDEPS[@]} -eq 0 ] && return 0
  for MAKEDEP in ${MAKEDEPS[@]}; do
    if [ "$MAKEDEP" != "$pkgname" ] &&
       ! is_installed $MAKEDEP &&
       ! is_provided $MAKEDEP; then
      if [ "$BUILDDEPS" != "TRUE" ]; then
        MAKEDEPS_MISSING=${MAKEDEPS_MISSING}${MAKEDEP}" "
        CHECK_MAKEDEP=1
        continue
      fi
      (( INDEX_DEP++ ))
      print_building_dep $MAKEDEP
      LOG_FILE=${TMP_PATH}/${CMD_BIN}/${CMD_BIN}-${pkgname}.log \
      PACMAN_FLAGS="--asdeps" VERBOSE=$VERBOSE EDIT_PKGBUILD=$EDIT_PKGBUILD \
      GCCINFO="" \
      DEPENDENCY_OF="makedependency of $pkgname" PACMAN_FLAGS="--asdeps"
      if [ "$NOCONFIRM" != "TRUE" ]; then
        $CMD_BIN --notitle --noresume --builddeps --install $MAKEDEP || CHECK_MAKEDEP=1
      else
        $CMD_BIN --notitle --noresume --noconfirm --builddeps --install $MAKEDEP || CHECK_MAKEDEP=1
      fi
      rm -rf $LOG_FILE
      [ $CHECK_MAKEDEP -eq 1 ] && return 1
      MAKEDEPS_INSTALLED=${MAKEDEPS_INSTALLED}${MAKEDEP}" "
    fi
  done
  return $CHECK_MAKEDEP
}

function get_pkg_repo()
{
  REPO=`LC_ALL=C $PACMANBIN -Si ${1} 2> /dev/null \
        | grep -m1 Repository \
        | awk -F": " '{ print $2 }'`
  case $REPO in
    core) ;;
    extra) ;;
    testing) ;;
    community) ;;
    community-testing) ;;
    *) REPO="aur" ;;
  esac
}

function get_pkgbuild()
{
  get_pkg_repo ${1}
  if [ "$REPO" != "aur" ]; then
    #abs; cp -rf /var/abs/${REPO}/${1} ${BUILD_DIR}
    $SYNCCMD $SYNCARGS ${SYNCSERVER}::abs/{${CARCH},any}/${REPO}/${1} ${BUILD_DIR}
    if [ ! -f ${BUILD_DIR}/${1}/PKGBUILD ]; then
      REPO="aur"
    fi
  fi
  if [ "$REPO" == "aur" ]; then
    cd $BUILD_DIR
    wget http://aur.archlinux.org/packages/${1}/${1}.tar.gz || return 1
    tar xfz ./${1}.tar.gz
    rm -rf ./${1}.tar.gz
  fi
  cd ${BUILD_DIR}/${1}
  return 0
}

function get_last_pkgbuild()
{
  if [ "$FULLDEPLIST" = "TRUE" ]; then
      if [ -f ${BUILD_DIR}/${1}/PKGBUILD ]; then
        cd ${BUILD_DIR}/${1}
      else
        get_pkgbuild $1 || return 1
      fi
  else
      get_pkgbuild $1 || return 1
  fi
  return 0
}

function get_pkginfo()
{
  unset arch pkgname pkgver pkgrel
  source ./PKGBUILD
}

# END_SECTION - pkgbuild parsing and editing
# BEGIN_SECTION - fetch pacman database

function get_local_version()
{
  grep -srl --line-regexp --include="desc" "$1" "$PACMANROOT/local" \
  | xargs grep -A 1 "^%VERSION%$" | tail -n 1
}

function get_local_pkgrel()
{
  get_local_version $1 | awk -F"-" '{print $2}'
}

function get_local_pkgver()
{
  get_local_version $1 | awk -F"-" '{print $1}'
}

function is_installed()
{
  if grep -qrl --include="desc" "^$1$" "${PACMANROOT}/local"; then
    return 0;
  else
    return 1;
  fi
}

function is_installed_cached()
{
  if [ `$PACMANBIN -Q ${1} 2> /dev/null | wc -l` != "0" ]; then
    return 0;
  else
    return 1;
  fi
}

function is_newer()
{
  local VER=( $(echo $1 | tr "[:punct:]" "\ " | sed 's/[a-zA-Z]/ &/g') )
  local LVER=( $(echo $2 | tr "[:punct:]" "\ " | sed 's/[a-zA-Z]/ &/g') )
  if [ ${#VER[@]} -gt ${#LVER[@]} ]; then
    VERLENGTH=${#VER[@]}
  else
    VERLENGTH=${#LVER[@]}
  fi

  for I_INDEX in `seq 0 $((${VERLENGTH}-1))`; do
    if `is_num ${VER[$I_INDEX]}` &&
       `is_num ${LVER[$I_INDEX]}`; then
      if [ ${VER[$I_INDEX]} -eq ${LVER[$I_INDEX]} ]; then
        continue;
      fi
      if [ ${VER[$I_INDEX]} -gt ${LVER[$I_INDEX]} ]; then
        return 0;
      else
        return 1;
      fi
      break
    elif [ `is_num ${VER[$I_INDEX]}` -ne `is_num ${LVER[$I_INDEX]}` ]; then
      if [ "${VER[$I_INDEX]}" = "${LVER[$I_INDEX]}" ]; then
        continue;
      fi
      if [ "${VER[$I_INDEX]}" \> "${LVER[$I_INDEX]}" ]; then
        return 0;
      else
        return 1;
      fi
      break
    fi
  done
  return 1
}

function is_newer_or_equal()
{
  local VER=( $(echo $1 | tr "[:punct:]" "\ " | sed 's/[a-zA-Z]/ &/g') )
  local LVER=( $(echo $2 | tr "[:punct:]" "\ " | sed 's/[a-zA-Z]/ &/g') )
  if [ ${#VER[@]} -gt ${#LVER[@]} ]; then
    VERLENGTH=${#VER[@]}
  else
    VERLENGTH=${#LVER[@]}
  fi

  for I_INDEX in `seq 0 $((${VERLENGTH}-1))`; do
    if `is_num ${VER[$I_INDEX]}` &&
       `is_num ${LVER[$I_INDEX]}`; then
      if [ ${VER[$I_INDEX]} -eq ${LVER[$I_INDEX]} ]; then
        if [ $I_INDEX -eq $((${VERLENGTH}-1)) ]; then
          return 0
        fi
        continue;
      fi
      if [ ${VER[$I_INDEX]} -gt ${LVER[$I_INDEX]} ]; then
        return 0;
      else
        return 1;
      fi
      break
    elif [ `is_num ${VER[$I_INDEX]}` -ne `is_num ${LVER[$I_INDEX]}` ]; then
      if [ "${VER[$I_INDEX]}" = "${LVER[$I_INDEX]}" ]; then
        if [ $I_INDEX -eq $((${VERLENGTH}-1)) ]; then
          return 0
        fi
        continue;
      fi
      if [ "${VER[$I_INDEX]}" \> "${LVER[$I_INDEX]}" ]; then
        return 0;
      else
        return 1;
      fi
      break
    fi
  done
  return 1
}

function is_num()
{
  if let $1 2>/dev/null; then
    return 0;
  else
    return 1;
  fi
}

function is_older()
{
  if ! is_newer_or_equal $1 $2; then
    return 0
  else
    return 1
  fi
}

function is_older_or_equal()
{
  if ! is_newer $1 $2; then
    return 0
  else
    return 1
  fi
}

function is_provided()
{
  local CANDIDATES=(`grep -srl --line-regexp --include="depends" \
                     "$1" "${PACMANROOT}/local"`)
  for FILE in ${CANDIDATES[@]};do
    if echo $(cat $FILE) | grep -q "%PROVIDES%.*$1"; then
      return 0;
    else
      continue;
    fi
  done
  return 1
}

function refresh_database()
{
  launch_with_su "$PACMANBIN --sync --refresh" &> /dev/null
}

function pkgs_in_group()
{
  sed -i '/'$1'/d' $LOG_FILE
  for PACKAGE in `$PACMANBIN -Sqg $1`; do
	PACKAGES[${#PACKAGES[@]}]=$PACKAGE
    echo $PACKAGE >> $LOG_FILE
	return 0
  done
}


# END_SECTION - fetch pacman database
# BEGIN_SECTION - manage package installation

function print_checking_deps()
{
  printf "${YELL}$(gettext "Checking dependencies...")${DEFA}\n"
}

DEPLIST_DEPS=()

function is_in_deplist()
{
  local DEP
  for DEP in ${DEPLIST_DEPS[@]}; do
    [ "$DEP" = "$1" ] && return 0
  done
  return 1
}

function list_package_depends()
{
  local -a DEPS
  local -a FINAL_DEPS
  local DEP
  unset depends pkgname
  CHECK_DEP=0

  source ./PKGBUILD
  for DEP in $(echo "${depends[@]}" | tr -d '\\'); do
    DEPS[${#DEPS[@]}]=$(echo $DEP | sed 's/=.*//' \
                                  | sed 's/>.*//' \
                                  | sed 's/<.*//')
  done
  [ ${#DEPS[@]} -eq 0 ] && return 0

  for DEP in ${DEPS[@]}; do
    if [ "$DEP" != "$pkgname" ] &&
       ! is_installed $DEP &&
       ! is_provided $DEP &&
       ! is_in_deplist $DEP; then
      [ "$BUILDDEPS" != "TRUE" ] && return 1
      FINAL_DEPS[${#FINAL_DEPS[@]}]=$DEP
      DEPLIST_DEPS[${#DEPLIST_DEPS[@]}]=$DEP
    fi
  done

  [ ${#FINAL_DEPS[@]} -eq 0 ] && return 0
  printf "\r$1 ${YELL}$(gettext "depends on"):${DEFA} "
  echo ${FINAL_DEPS[@]}
  for DEP in ${FINAL_DEPS[@]}; do
      CHECK_FOUND=0
      CHECK_DEPS=0
      get_pkgbuild $DEP &> /dev/null || CHECK_FOUND=1
      if [ -f ${BUILD_DIR}/${DEP}/PKGBUILD ]; then
        list_package_depends $DEP || CHECK_DEPS=1
      else
      printf "\r${LRED}$(gettext "Missing PKGBUILD for"):${DEFA} ${DEP}\n"
      fi
  done
}

function list_all_package_depends()
{
  local CURRENT_PKG
  [ "$NOTITLE" = "TRUE" ] || print_checking_deps
  for CURRENT_PKG in $PACKAGES; do
      CHECK_FOUND=0
      CHECK_DEPS=0
      printf "\r                            \r${CURRENT_PKG}"
      get_pkgbuild $CURRENT_PKG &> /dev/null || CHECK_FOUND=1
      if [ -f ${BUILD_DIR}/${CURRENT_PKG}/PKGBUILD ]; then
        list_package_depends $CURRENT_PKG || CHECK_DEPS=1
      fi
  done
  printf "\r                                   \r"
}

function build_multiple_packages()
{
  local CPKG
  check_log_file
  sed -i '/^$/d' $LOG_FILE
  ERROR_PKGS=""
  INDEX_PKG=1

  if [ "$PKG_MATCH" = "TRUE" ]; then
      PACKAGES=`grep -P "$PKG_REGEX" $LOG_FILE`
      NUM_PKGS=`grep -P "$PKG_REGEX" $LOG_FILE | wc -l`
  else
      PACKAGES=`cat $LOG_FILE`
      NUM_PKGS=`cat $LOG_FILE | wc -l`
  fi

  if [ "$PRETEND" = "TRUE" ]; then
      printf "${YELL}$(gettext "The following packages would be built:")${DEFA}\n"
      for CPKG in $PACKAGES; do
        msg "$CPKG"
      done
      return 0
  elif [ "$NOTITLE" != "TRUE" ]; then
      printf "${YELL}$(gettext "The following packages will be built:")${DEFA}\n"
      for CPKG in $PACKAGES; do
        msg "$CPKG"
      done
  fi

  [ "$FULLDEPLIST" = "TRUE" ] && list_all_package_depends

  if [ "$NOCONFIRM" != "TRUE" ]; then
      printf "${BOLD} $(gettext "Do you want to continue? (Y/n):")${DEFA} "
      read CONFIRM_ANSWER
      if [ "$CONFIRM_ANSWER" = "n" ]; then
        printf "${LRED} $(gettext "Exiting...")\n ${DEFA}"
        exit 0
      fi
  fi

  # TODO: order packages in a way that pkgs which should be installed explicitly
  #       but are pulled in as deps TOO are not installed twice, and are installed
  #       before they're pulled in with --asdeps!

  for CURRENT_PKG in $PACKAGES; do
    CHECK_DEPS=0
    CHECK_FOUND=0
	CHECK_GROUP=0
    CHECK_INSTALL=0
    CHECK_MAKEPKG=0
    CHECK_UNINSTALL=0
    INDEX_DEP=0
    print_building
    if [ "`$PACMANBIN -Qqd $CURRENT_PKG 2> /dev/null`" != "" ]; then
      PACMAN_FLAGS="--asdeps"
    else
      PACMAN_FLAGS="--asexplicit"
    fi

    get_last_pkgbuild $CURRENT_PKG &> /dev/null || CHECK_GROUP=1

    if [ $CHECK_GROUP -eq 1 ]; then
      pkgs_in_group $CURRENT_PKG || CHECK_FOUND=1
    else
      if [ -f ${BUILD_DIR}/${CURRENT_PKG}/PKGBUILD ]; then
        cd ${BUILD_DIR}/${CURRENT_PKG}
        edit_pkgbuild
        get_package_makedepends || CHECK_DEPS=2
        get_package_depends || CHECK_DEPS=1
        [ $CHECK_DEPS -ne 0 ] && CHECK_INSTALL=1
	    [ $UID -eq 0 ] && ASROOT_FLAG="--asroot"
	    if [ $CHECK_DEPS -eq 0 ]; then
          if [ "$VERBOSE" != "TRUE" ]; then
            makepkg -Af --noconfirm $ASROOT_FLAG $NOCOLOR_FLAG &> /dev/null || \
            CHECK_MAKEPKG=1
            get_pkginfo
          else
            printf "\n"
            makepkg -Af --noconfirm $ASROOT_FLAG $NOCOLOR_FLAG || CHECK_MAKEPKG=1
            get_pkginfo
            print_installing
          fi
          [ "$FORCE" == "TRUE" ] && FORCE_FLAG="--force"
          launch_with_su "$PACMANBIN -U --noconfirm $FORCE_FLAG $PACMAN_FLAGS \
            ${PKGDEST}/$pkgname-$pkgver-$pkgrel-*${PKGEXT}" &> /dev/null \
            || CHECK_INSTALL=1
        fi
      else
        CHECK_INSTALL=1
      fi
      if [ $CHECK_INSTALL -eq 0 ]; then
        if [ "${#EXPORT_DIR}" -gt 0 ]; then
          cp "${PKGDEST}/$pkgname-$pkgver-$pkgrel-*${PKGEXT}" "$EXPORT_DIR/"
        fi
        rm -rf ${BUILD_DIR}/${CURRENT_PKG} &> /dev/null
        sed -i '/'${CURRENT_PKG}'/d' $LOG_FILE
      else
        ERROR_PKGS=${ERROR_PKGS}${CURRENT_PKG}" "
      fi
      print_checkinstall $CHECK_INSTALL $CHECK_DEPS $CHECK_MAKEPKG $CHECK_FOUND $CHECK_GROUP
      (( INDEX_PKG++ ))
	fi
  done
    if [ "$KEEPDEPS" != "TRUE" ] &&
       [ -n "$MAKEDEPS_INSTALLED" ]; then
      print_uninstalling_deps
      launch_with_su "$PACMANBIN -Rd --noconfirm $MAKEDEPS_INSTALLED" \
      &> /dev/null || CHECK_UNINSTALL=1
      print_checkuninstall $CHECK_UNINSTALL
    fi
    if [ -n "$ERROR_PKGS" ]; then
      if [ -z "$DEPENDENCY_OF" ]; then
        print_errorpkgs
      fi
      return 1
    else
      delete_log
    fi
  return 0
}

function check_log_file()
{
  if [ ! -f $LOG_FILE ]; then
    print_no_log_file
    exit 0
  fi
}

function clean()
{
  print_clean
  read DELETE_ANSWER
  printf "\n"
  if [ "$DELETE_ANSWER" == "y" ]; then
    delete_log && delete_tmp
  fi
}

function delete_log()
{
  if [ -f $LOG_FILE ]; then
    rm -rf $LOG_FILE &> /dev/null
  fi
}

function delete_tmp()
{
  if [ -d ${TMP_PATH}/${CMD_BIN} ]; then
    rm -rf ${TMP_PATH}/${CMD_BIN} &> /dev/null
  fi
}

function match_repo()
{
  delete_log
  INDEX_PKG=0
  for CURRENT_PKG in $@; do
    REPO=`LC_ALL=C $PACMANBIN -Si $CURRENT_PKG 2> /dev/null \
          | grep -m1 Repository \
          | awk -F": " '{ print $2 }'`
    case $REPO in
      core) ;;
      extra) ;;
      testing) ;;
      community) ;;
      community-testing) ;;
      *) REPO="aur" ;;
    esac
    for CURRENT_REPO in $REPO; do
      for CURRENT_TARGET_REPO in $TARGET_REPO; do
        if [ "$CURRENT_REPO" == "$CURRENT_TARGET_REPO" ]; then
          echo $CURRENT_PKG >> $LOG_FILE
        fi
      done
    done
  done
  PACKAGES=`cat $LOG_FILE`
}

# END_SECTION - select packages
# BEGIN_SECTION - start building by type

function build_type()
{
  if [ -n "$TARGET_REPO" ]; then
    check_log_file
    print_repo "$TARGET_REPO\n"
    print_match_repo
    match_repo `cat $LOG_FILE`
    print_success "done"
    printf "\n"
  else
    print_repo "$(gettext "build from every repository")\n"
  fi
  build_multiple_packages
}

function install()
{
  if [ "$INSTALLING" == "TRUE" ]; then
    delete_log
    for EXTRA_FLAG in $INSTALLPKGS; do
      echo $EXTRA_FLAG >> $LOG_FILE
    done
    build_multiple_packages
    exit 0
  fi
}

function read_type()
{
  if [ -z "$TYPES_ENABLED" ] && [ -n "$REPO_ENABLED" ]; then
    printf "$(gettext "No type specified, assuming world...")\n\n"
    ENABLED_TYPES="world"
    sleep 1
  elif [ -n "$TYPES_ENABLED" ] && [ -z "$REPO_ENABLED" ]; then
    printf "$(gettext "No repository specified, build all...")\n\n"
    sleep 1
  fi
  for EXTRA_TYPE in $ENABLED_TYPES; do
    case $EXTRA_TYPE in
      deps) type_deps ;;
      explicit) type_explicit ;;
      world) type_world ;;
      devel) type_devel ;;
    esac
  done
}

function resume()
{
  if [ -f $LOG_FILE ]; then
    print_resume_detected
    read RESUME_ANSWER
    if [ "$RESUME_ANSWER" != "n" ]; then
      printf "\n"
      printf "$(gettext "Resuming...")\n"
      build_multiple_packages
      exit 0
    else
      print_resume_overwrite
      read RESUME_OW_ANSWER
      printf "\n"
      if [ "$RESUME_OW_ANSWER" != "y" ]; then
        printf "$(gettext "Exiting...")\n"
        exit 0
      fi
    fi
  fi
}

function sysupgrade()
{
  if [ "$SYSUPGRADE" == "TRUE" ]; then
    delete_log
    print_refreshing_database
    refresh_database
    sysupgrade_local
    sysupgrade_aur
    TYPES_ENABLED="TRUE"
    print_success "done"
    printf "\n"
    if [ ! -f $LOG_FILE ]; then
      printf "$(gettext "System")"
      print_success "$(gettext "Up-to-date")"
    else
      print_type "$(gettext "both deps and explicit packages")"
      build_type
    fi
    exit 0
  fi
}

function sysupgrade_aur()
{
  for PACKAGE in $($PACMANBIN -Qqm); do
    AUR_VERSION=`get_json_version $PACKAGE`
    LOCAL_VERSION=`get_local_version $PACKAGE`
    if `is_newer $AUR_VERSION $LOCAL_VERSION`; then
      echo $PACKAGE >> $LOG_FILE
    fi
  done
}

function sysupgrade_local()
{
  PACKAGES=(`launch_with_su "$PACMANBIN --sync --sysupgrade --print-uris" \
             | grep "^\(ftp:\/\/\|http:\/\/\|file:\/\/\)" \
             | sed -e "s/-i686.pkg.tar.gz$//" \
                   -e "s/-x86_64.pkg.tar.gz$//" \
                   -e "s/-any.pkg.tar.gz$//" \
                   -e "s/.pkg.tar.gz//" \
                   -e "s/-i686.pkg.tar.xz$//" \
                   -e "s/-x86_64.pkg.tar.xz$//" \
                   -e "s/-any.pkg.tar.xz$//" \
                   -e "s/.pkg.tar.xz//" \
                   -e "s/^.*\///" \
                   -e "s/-[^-]*-[^-]*$//"`)
  for PACKAGE in ${PACKAGES[@]}; do
    echo $PACKAGE >> $LOG_FILE
  done
}

function type_deps()
{
  $PACMANBIN -Qqd > $LOG_FILE
  if [ "$DONE_DEPS" != "TRUE" ]; then
    print_type "$(gettext "deps packages")"
    build_type
  fi
  DONE_DEPS="TRUE"
}

function type_explicit()
{
  $PACMANBIN -Qqe > $LOG_FILE
  if [ "$DONE_EXPLICIT" != "TRUE" ]; then
    print_type "$(gettext "explicit packages")"
    build_type
  fi
  DONE_EXPLICIT="TRUE"
}

function type_world()
{
  $PACMANBIN -Qq > $LOG_FILE
  if [ "$DONE_WORLD" != "TRUE" ]; then
    print_type "$(gettext "both deps and explicit packages")"
    build_type
  fi
  DONE_DEPS="TRUE"
  DONE_EXPLICIT="TRUE"
  DONE_WORLD="TRUE"
}

function type_devel()
{
  $PACMANBIN -Qq | grep "\-\(svn\|cvs\|hg\|git\|bzr\|darcs\)" > $LOG_FILE
  if [ "$DONE_DEVEL" != "TRUE" ]; then
    print_type "$(gettext "recompile only installated devel packages")"
    build_type
  fi
  DONE_DEPS="TRUE"
  DONE_EXPLICIT="TRUE"
  DONE_WORLD="TRUE"
}

function search()
{
  MATCHED=`$PACMANBIN -Ss $PKG_REGEX`
  line=`wget -q "http://aur.archlinux.org/packages.php?setlang=en&do_Search=SeB=nd&L=2&C=0&PP=100&K=$PKG_REGEX" -O - \
      | grep -A 2 "<a href='/packages.php?ID=" | sed -e "s/<\/span>.*$//" -e "s/^.*packages.php?ID=.*span class.*'>/aur\//" \
      -e "s/^.*span class.*'>//" | grep -v "&nbsp;" | grep -v "^--"`
  if [ "${#line}" -gt 0 ]; then
    package=$(echo $line | awk '{ print $1}' | sed 's/^.*\///')
    version=$(echo $line | awk '{print $2}')
    MATCHED="${MATCHED} aur/${package} ${version}\n"
  fi
  printf "$MATCHED \n"
}

# END_SECTION - select packages
# BEGIN_SECTION - application run

function main()
{
  source gettext.sh
  environment
  check_required_binaries
  check_args $@
  colorize_output
  [ "$NOTITLE" != "TRUE" ] && print_title
  [ "$HELP" == "TRUE" ] && print_help && exit
  [ "$CLEAN" == "TRUE" ] && clean
  [ "$GCCINFO" == "TRUE" ] && print_gcc_info
  [ "$NORESUME" != "TRUE" ] && resume
  [ "$SYSUPGRADE" == "TRUE" ] && sysupgrade
  if [ "$INSTALLING" == "TRUE" ]; then
    if [ "$SEARCH" == "TRUE" ]; then
      search
    else
      install
    fi
  fi

  read_type
}

main $@

# END_SECTION - application run

# ex:ts=2:et:
